<#
.SYNOPSIS
Creates bulk upload command file to help bulk upload RDL report files for TFS server which configured reporting with SSRS already.

.DESCRIPTION
The bulk upload command file created by this tool uses TFS power tool to upload the RDL reports for existing projects from the process template file manually dropped in the same working folder.

It is important to understand:
- The installation of 'Microsoft Team Foundation Server 2013 Power Tools' is required 
  in order to successfully execute the bulk upload command file.
- The bulk upload command operates on best effort basis, and will overwrite any report 
  and Reporting Data source customizations.
- The bulk upload command does not recognize projects upgraded from TFS 2010, because 
  they do not have necessary metadata.
- It is required to manually drop the process template file at the same location as 
  bulk upload command. The template needs to be renamed to the same template name used 
  by target project and include the latest or desired RDL reports. The bulk upload 
  command will only upload the RDL reports.
- The bulk upload command will still work without the process template file exists in 
  the same location. In this case, it will only refresh the RDL reports with the same 
  version that currently exists in the process template used by the project.
#> 

Param (
    [Uri] $ProjectCollectionUri=$(Read-Host -prompt "Please specify the uri of TeamFoundation project collection.")
)

$TfsProjectCollection
$Css
$Projects
$WellFormedProjects = @()
$ProjectTemplateMap = @{}

function LogMajorAction
{
    Write-Host "$input" -ForegroundColor Cyan
}

function LogError
{
    Write-Host "$input" -ForegroundColor Red
}

function LogWarning
{
    Write-Host "$input" -ForegroundColor Yellow
}

function LogSuccess
{
    Write-Host "$input" -ForegroundColor Green
}

function LogStepHeader
{
    Write-Host "`n---------------------------------------------"
    "`t$input" | LogMajorAction
    Write-Host "---------------------------------------------"
}

function LogInitialMessages
{
    "Reporting bulk update tool for TFS 2013) started." | LogMajorAction
    Write-Host "`tTarget project collection uri: $ProjectCollectionUri"
    Write-Host "`tFor more details about using the tool, run: Get-Help .\ReportingBulkUpdateToolForTfs2013.ps1"
}

function LogCompletionMessages
{
    "`nReview the logs, and ensure all relevant projects are accounted for, and not filtered out." | LogMajorAction
    "Once you are satisfied, review the affected projects in the bulk upload script, and run it." | LogMajorAction
    "`nWarning:" | LogWarning
    "`tThe tool operates on best effort basis, and will overwrite any report and Reporting Data source customizations." | LogWarning
    "`tThe tool does not recognize projects upgraded from TFS 2010, because they do not have necessary metadata." | LogWarning
    "`tPlease ensure the project collection does have reporting configured, otherwise the upload command will fail." | LogWarning
}

function LoadTfsDependencies
{
    $Dependencies = @("Microsoft.TeamFoundation.Client","Microsoft.TeamFoundation.Common")
    foreach ($Dependency in $Dependencies)
    {
        Add-Type -AssemblyName "$Dependency, Version=12.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
    }
}

function ConnectTfsServer
{
    "Connecting TeamFoundation Server" | LogStepHeader
    $Script:TfsProjectCollection = [Microsoft.TeamFoundation.Client.TfsTeamProjectCollectionFactory]::GetTeamProjectCollection($ProjectCollectionUri)
    $TfsProjectCollection.EnsureAuthenticated()
    if ($TfsProjectCollection.HasAuthenticated)
    {
        Write-Host "TeamFoundation connection is established."
    }
    else
    {
        Write-Error "TeamFoundation connection is not authenticated."
        exit
    }
}

function EnumerateTeamProjects
{
    "Enumerating Team Projects" | LogStepHeader
    $Script:Css = $TfsProjectCollection.GetService([Microsoft.TeamFoundation.Server.ICommonStructureService4])
    $Script:Projects = $Css.ListAllProjects()
    if ($Projects.Length -le 0)
    {
        "No project found." | LogError
        exit
    }
    else
    {
        Write-Host "$($Projects.Length) project(s) found."
    }
}

function FilterWellFormedProjects
{
    "Filtering Well-Formed Projects" | LogStepHeader
    foreach ($Project in $Projects)
    {
        if ($Project.Status -ne [Microsoft.TeamFoundation.Common.ProjectState]::WellFormed)
        {
            "Project '$($Project.Name)' is not well-formed, and is being ignored." | LogWarning
        }
        else
        {
            $Script:WellFormedProjects += $Project
        }
    }
    
    if ($WellFormedProjects.Length -le 0)
    {
        "No well-formed project found." | LogError
        exit
    }
    else
    {
        Write-Host "$($WellFormedProjects.Length) project(s) accepted."
    }
}

function EnsureProjectTemplateAvailability
{
    "Ensuring Template Property Availability" | LogStepHeader
    foreach ($WellFormedProject in $WellFormedProjects)
    {
        $ProjectProcessTemplateProperty = $Css.GetProjectProperty($WellFormedProject.Uri, "Process Template")
        if ($ProjectProcessTemplateProperty -eq $null)
        {
            "Project '$($WellFormedProject.Name)' does not have necessary process template information defined, and is being ignored." | LogWarning
        }
        else
        {
            $Script:ProjectTemplateMap.Add($WellFormedProject, $ProjectProcessTemplateProperty.Value)
        }
    }
    
    if ($ProjectTemplateMap.Count -le 0)
    {
        "No available project template found." | LogError
        exit
    }
    else
    {
        Write-Host "$($ProjectTemplateMap.Count) project(s) accepted."
    }
}

function WriteUploadCommands
{
    "Writing Upload Commands" | LogStepHeader
    $OutputDirectory = $PSScriptRoot
    $UploaderFilePath = Join-Path $OutputDirectory "BulkUpload.cmd"
    Write-Host "Writing commands for identified project(s) to '$UploaderFilePath'"
    $CommandBuilder = New-Object System.Text.StringBuilder
    foreach ($AcceptProjectTemplateMap in $ProjectTemplateMap.GetEnumerator())
    {
        [void]$CommandBuilder.AppendLine("tfpt AddProjectReports /collection:`"$ProjectCollectionUri`" /teamproject:`"$($AcceptProjectTemplateMap.Key.Name)`" /processTemplate:`"$($AcceptProjectTemplateMap.Value)`" /force")
        
        if ((Test-Path $AcceptProjectTemplateMap.Value) -eq $false)
        {
            "'$($AcceptProjectTemplateMap.Value)' process template folder not found at current location.`n`tPlease make sure that a folder named after the target process template is placed at the same location as bulk upload command, and it contains the template file including the desired RDL report(s)." | LogWarning
        }
    }
    [System.IO.File]::WriteAllText($UploaderFilePath, $CommandBuilder.ToString())
    "`nUpload commands file created" | LogSuccess
}

# Log initial messages
LogInitialMessages

# Load TeamFoundation assemblies
LoadTfsDependencies

# Step 0 - Connecting to TFS
ConnectTfsServer

# Step 1 - Enumerating all team projects
EnumerateTeamProjects

# Step 2 - Filtering for well-formed projects
FilterWellFormedProjects

# Step 3 - Ensuring template property is available
EnsureProjectTemplateAvailability

# Step 4 - Writing upload commands for identified project(s) to current location
WriteUploadCommands

# Step 5 - Completion messages
LogCompletionMessages